/**
 * 
 */
package com.cmcc.volte.xdr.SigXDR;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.cmcc.volte.xdr.XDRUtil.XDRUtil;

/**
 * @author lvpin
 * @date 20161123
 * 这里为了尽量保持原来的代码逻辑不修改加的重现组装IP分片的功能
 * 20161124 TODO 应该整体调整这部分架构，界面上的流程图应该基于已经生成PCAP来加载比较合乎逻辑，且生成PCAP和通过PCAP反向生成流程图的逻辑可以共用
 * 20161227 TODO 同时支持8100 & 0800包结构   DONE
 */
public class SigXDRReassemble {
	private static Logger logger = LogManager.getLogger(SigXDRReassemble.class);
	
	public static SigXDR reassemble(SigXDR sigXDR){
		
		ArrayList<ReassembleAgent> reassembleAgentList = new ArrayList<ReassembleAgent>();//分片的多个包只对应一个ReassembleAgent，不分片的包每个对应一个ReassembleAgent
		
		List<SigXDRData> sigXDRDataList = sigXDR.getData();
		//int i=1;//only for systemout test
		for(SigXDRData sigXDRData : sigXDRDataList){

			byte[] loadBytes = sigXDRData.getLoad();
			
			
			int moveSize = 0;
			byte[] enthernetType = new byte[2];//Enthernet 头域的Type字段
			//前12个就是目的和源mac
			enthernetType[0] = loadBytes[12];
			enthernetType[1] = loadBytes[13];
			String entherNetTypeStr = XDRUtil.bytesToHexString(enthernetType);
			//以上取法基于TCP/IP协议族标准
			//第13、14字节指明了其上承载的是什么，可以是IP，可能是VLAN
			
			byte[] virtuallanType = null;
			String virtuallanTypeStr = null;
			
			if("8100".equals(entherNetTypeStr)){//处理8100即VirtualLan情况的数据包,会多4个字节的VirtualLan头域,需要向后移动4位才能获得实际数据
				moveSize = 4;
				virtuallanType = new byte[2];
				virtuallanType[0] = loadBytes[16];
				virtuallanType[1] = loadBytes[17];
				virtuallanTypeStr = XDRUtil.bytesToHexString(virtuallanType);
			}
            
			if("0800".equals(entherNetTypeStr) || "0800".equals(virtuallanTypeStr)){//IPv4
				//logger.info("0800");
				byte[] ipByte56 = new byte[2];
				ipByte56 = Arrays.copyOfRange(loadBytes, 18+moveSize, 20+moveSize);
				int identification = bytesToInt(ipByte56);//该头占2个byte，在IP头的第5和6个byte，
				
				byte ipByte7 = loadBytes[20+moveSize];
				int flags = ipByte7 >> 5;//该头占3个bit，在IP头的第7个byte的高位3个bit，最高位无效，0x00表示分片的最后一个 、 0x01表示need more frag、0x02表示不能再分片
				
				byte[] ipByte78 = new byte[2];
				ipByte78 = Arrays.copyOfRange(loadBytes, 20+moveSize, 22+moveSize);
				int ipByte78Int =  bytesToInt(ipByte78);
				int offset= ipByte78Int & 8191;//该头占13个bit，在IP头的第7和8个byte,8191即0001111111111111
						
				SigXDRReassemble sigXDRReassemble = new SigXDRReassemble();
				SigXDRReassemble.SigXDRDataAgent sigXDRDataAgent = sigXDRReassemble.new SigXDRDataAgent(identification,flags,offset,sigXDRData,moveSize);
				
				
//				System.out.println("-------------------"+i++);
//				System.out.println("Len : "+sigXDRData.getLen());
//				System.out.println("Time_s : "+sigXDRData.getTime_s());
//				System.out.println("Time_ns : "+sigXDRData.getTime_ns());
//				System.out.println("Load.length : "+sigXDRData.getLoad().length);
//				System.out.println("-------------------");			
//				System.out.println("identification : "+identification);
//				System.out.println("flags : "+flags);
//				System.out.println("offset : "+offset);
//				System.out.println("moveSize : "+moveSize);
				
				storeSigXDRDataAgent_3(reassembleAgentList,sigXDRDataAgent,flags,offset);
				
				
			}else if("86dd".equals(entherNetTypeStr) || "86dd".equals(virtuallanTypeStr)){//IPv6
				//logger.info("86dd");
				int packgeType = 1;
				byte ipv6Byte7 = loadBytes[20+moveSize];
				int nextHeader = ipv6Byte7;
				int offset = 0 ;
				int flag = 0 ;
				String identificationForIPv6 = null;
				if(nextHeader == 44){//Fragmentation Header
					byte ipv6FragmentHeaderByte1 = loadBytes[54 + moveSize];
					int fragmentationNextHeader = ipv6FragmentHeaderByte1;					
					
					byte[] ipv6FragmentHeaderByte34 = Arrays.copyOfRange(loadBytes, 56+moveSize, 58+moveSize);
					int ipv6FragmentHeaderByte34Int =  bytesToInt(ipv6FragmentHeaderByte34);					
					offset = (ipv6FragmentHeaderByte34Int & 0xFFF8) / 8;//65528=0xFFF8
					flag = ipv6FragmentHeaderByte34Int & 1;
					
					byte[] ipv6FragmentHeaderByte5678 = Arrays.copyOfRange(loadBytes, 58+moveSize, 62+moveSize);
					identificationForIPv6 = XDRUtil.bytesToHexString(ipv6FragmentHeaderByte5678);

					moveSize = moveSize + 8;//8个字节Fragmentation Header
					if(fragmentationNextHeader == 50){//Fragmentation Header 后是 ESP Header
						packgeType = 2;
					}
				}else if(nextHeader == 50 ){//IP Header 后是 ESP Header
					packgeType = 2;
				}
				
				SigXDRReassemble sigXDRReassemble = new SigXDRReassemble();
				SigXDRReassemble.SigXDRDataAgent sigXDRDataAgent = sigXDRReassemble.new SigXDRDataAgent(identificationForIPv6,flag,offset,sigXDRData,moveSize,packgeType);
				
//				System.out.println("-------------------"+i++);
//				System.out.println("Len : "+sigXDRData.getLen());
//				System.out.println("Time_s : "+sigXDRData.getTime_s());
//				System.out.println("Time_ns : "+sigXDRData.getTime_ns());
//				System.out.println("Load.length : "+sigXDRData.getLoad().length);
//				System.out.println("-------------------");			
//				System.out.println("identification : "+identificationForIPv6);
//				System.out.println("flags : "+flag);
//				System.out.println("offset : "+offset);
//				System.out.println("moveSize : "+moveSize);
				
				storeSigXDRDataAgentForIPv6(reassembleAgentList,sigXDRDataAgent,nextHeader);
			}else {
				logger.error("Unkonw Enthernet Type : "+entherNetTypeStr);
			}		

		}
		
		if(reassembleAgentList.size() == sigXDR.getHeader().getSize()) //没有分片直接返回
			return sigXDR;    
		
		//System.out.println("---------reassembleAgentList.size()----------"+reassembleAgentList.size());
		
		//-------重新拼装SigXDR  START--------
		SigXDR newSigXDR = new SigXDR();
		
		//拼装SigXDRHeader
		SigXDRHeader sigXDRHeader = new SigXDRHeader();
		sigXDRHeader.setSize((short)reassembleAgentList.size());//设置重新拼装后的SigXDR的Size
		sigXDRHeader.setInterface(sigXDR.getHeader().getInterface());
		newSigXDR.setHeader(sigXDRHeader);
		
		//拼装Data  即List<SigXDRData>
		List<SigXDRData> newSigXDRDataList = new ArrayList<SigXDRData>();
//		int j =1;//only for systemout
		for(ReassembleAgent reassembleAgent : reassembleAgentList){
			SigXDRData newSigXDRData = reassembleAgent.reassemble();//reassembleAgent.reassemble()是真正执行IP分片重新组装的方法
			newSigXDRDataList.add(newSigXDRData);
						
//			System.out.println("-------------------"+j++);			
//			System.out.println("Len : "+newSigXDRData.getLen());
//			System.out.println("Time_s : "+newSigXDRData.getTime_s());
//			System.out.println("Time_ns : "+newSigXDRData.getTime_ns());
//			System.out.println("Load.length : "+newSigXDRData.getLoad().length);
//			try {
//				System.out.println("Load : "+new String(newSigXDRData.getLoad(),"utf-8"));
//			} catch (UnsupportedEncodingException e) {
//				e.printStackTrace();
//			}
		}
		newSigXDR.setData(newSigXDRDataList);
		newSigXDR.setPackgeType(reassembleAgentList.get(0).getPackgeType());
		logger.info("SigXDRReassemble Completed,  Packge Type Is : "+ reassembleAgentList.get(0).getPackgeType()+" ( 0:IPv4, 1:IPv6, 2:IPv6+ESP) ");
		//-------重新拼装SigXDR  STOP--------
			
		return newSigXDR;
	}
	/**
	 * TODO 删除该方法
	 * @param reassembleAgentList
	 * @param sigXDRDataAgent
	 * @param flags
	 */
	public static void storeSigXDRDataAgent(ArrayList<ReassembleAgent> reassembleAgentList,SigXDRDataAgent sigXDRDataAgent,int flags){
		SigXDRReassemble sigXDRReassemble = new SigXDRReassemble();
		if(flags == 0x02 ){//等于0x02表示这个包不是IP分片的    
			ReassembleAgent reassembleAgent = sigXDRReassemble.new ReassembleAgent();
			reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);
			reassembleAgent.setIdentification(sigXDRDataAgent.getIdentification());			
			reassembleAgentList.add(reassembleAgent);
		}else{
			boolean firstIndentificaton = true;
			for(ReassembleAgent reassembleAgent : reassembleAgentList){
				if(reassembleAgent.getIdentification() == sigXDRDataAgent.getIdentification()){
					reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);
					firstIndentificaton = false;
				}
			}
			if(firstIndentificaton){
				ReassembleAgent reassembleAgent = sigXDRReassemble.new ReassembleAgent();
				reassembleAgent.setIdentification(sigXDRDataAgent.getIdentification());								
				reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);				
				reassembleAgentList.add(reassembleAgent);
			}
		}
	}
	/**
	 * TODO 删除该方法
	 * @param reassembleAgentList
	 * @param sigXDRDataAgent
	 * @param flags
	 */
	public static void storeSigXDRDataAgent_2(ArrayList<ReassembleAgent> reassembleAgentList,SigXDRDataAgent sigXDRDataAgent,int flags){
		SigXDRReassemble sigXDRReassemble = new SigXDRReassemble();

		boolean firstIndentificaton = true;
		for(ReassembleAgent reassembleAgent : reassembleAgentList){
			if(reassembleAgent.getIdentification() == sigXDRDataAgent.getIdentification()){
				reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);
				firstIndentificaton = false;
			}
		}
		if(firstIndentificaton){
			ReassembleAgent reassembleAgent = sigXDRReassemble.new ReassembleAgent();
			reassembleAgent.setIdentification(sigXDRDataAgent.getIdentification());								
			reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);				
			reassembleAgentList.add(reassembleAgent);
		}

	}
	/**
	 * 将属于同一个IP分片的包保存到同一个ReassembleAgent的List里
	 * 
	 * flags的3个bit从左开始 分别为    Reserved、DF(Don't Fragments)、MF(More Fragements)
	 * DF=1表示当前包不能再被分片了
	 * MF=1表示当前包还有后续分片，且此时offset为0的话，表示当前包为IP分片的第一个
	 * MF=0表示当前包要么是IP分片的最后一个，要么不是一个IP分片，需要进一步根据offset来判断，MF和offset同时为零时表示当前包不是一个分片。MF=0，offset不为零时表示当前包是一个IP分片的最后一个。
	 * 
	 * @param reassembleAgentList
	 * @param sigXDRDataAgent
	 * @param flags
	 * @param offset
	 */
	public static void storeSigXDRDataAgent_3(ArrayList<ReassembleAgent> reassembleAgentList,SigXDRDataAgent sigXDRDataAgent,int flags,int offset){
		int MF = flags & 0x0001;
		SigXDRReassemble sigXDRReassemble = new SigXDRReassemble();
		if(MF == 0 && offset == 0 ){// 非分片IP包   
			ReassembleAgent reassembleAgent = sigXDRReassemble.new ReassembleAgent();
			reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);
			reassembleAgent.setIdentification(sigXDRDataAgent.getIdentification());			
			reassembleAgentList.add(reassembleAgent);
		}else{
			boolean firstIndentificaton = true;
			for(ReassembleAgent reassembleAgent : reassembleAgentList){
				if(reassembleAgent.getIdentification() == sigXDRDataAgent.getIdentification()){
					reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);
					firstIndentificaton = false;
				}
			}
			if(firstIndentificaton){
				ReassembleAgent reassembleAgent = sigXDRReassemble.new ReassembleAgent();
				reassembleAgent.setIdentification(sigXDRDataAgent.getIdentification());								
				reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);				
				reassembleAgentList.add(reassembleAgent);
			}
		}
	}
	
	/**
	 * IPv6 Reassemble
	 * @param reassembleAgentList
	 * @param sigXDRDataAgent
	 * @param nextHeader
	 */
	public static void storeSigXDRDataAgentForIPv6(ArrayList<ReassembleAgent> reassembleAgentList,SigXDRDataAgent sigXDRDataAgent,int nextHeader){
		SigXDRReassemble sigXDRReassemble = new SigXDRReassemble();
		if(nextHeader != 44){//没有Fragmentation Header ，即非分片包
			ReassembleAgent reassembleAgent = sigXDRReassemble.new ReassembleAgent();
			reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);
			reassembleAgent.setIdentificationForIPv6(sigXDRDataAgent.getIdentificationForIPv6());
			reassembleAgent.setPackgeType(sigXDRDataAgent.getPackgeType());
			
			reassembleAgentList.add(reassembleAgent);
		}else {
			boolean firstIndentificaton = true;
			for(ReassembleAgent reassembleAgent : reassembleAgentList){
				if(reassembleAgent.getIdentificationForIPv6() != null && reassembleAgent.getIdentificationForIPv6() .equals(sigXDRDataAgent.getIdentificationForIPv6())){
					reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);
					firstIndentificaton = false;
				}
			}
			if(firstIndentificaton){
				ReassembleAgent reassembleAgent = sigXDRReassemble.new ReassembleAgent();
				reassembleAgent.setIdentificationForIPv6(sigXDRDataAgent.getIdentificationForIPv6());								
				reassembleAgent.getSigXDRDataAgentList().add(sigXDRDataAgent);
				reassembleAgent.setPackgeType(sigXDRDataAgent.getPackgeType());
				
				reassembleAgentList.add(reassembleAgent);
			}
		}
		
	}
	
	public class ReassembleAgent{
		int identification;
		String identificationForIPv6;
		boolean ifReassemble=false;
		int packgeType = 0;//0-IPv4(default),1-IPv6,2-IPv6+ESP
		ArrayList<SigXDRDataAgent> sigXDRDataAgentList = new ArrayList<SigXDRDataAgent>();//同一个包的多个分片
		
		public int getIdentification() {
			return identification;
		}
		public void setIdentification(int identification) {
			this.identification = identification;
		}
		public boolean isIfReassemble() {
			return ifReassemble;
		}
		public void setIfReassemble(boolean ifReassemble) {
			this.ifReassemble = ifReassemble;
		}
		public ArrayList<SigXDRDataAgent> getSigXDRDataAgentList() {
			return sigXDRDataAgentList;
		}
		public void setSigXDRDataAgentList(
				ArrayList<SigXDRDataAgent> sigXDRDataAgentList) {
			this.sigXDRDataAgentList = sigXDRDataAgentList;
		}
		public int getPackgeType() {
			return packgeType;
		}
		public void setPackgeType(int packgeType) {
			this.packgeType = packgeType;
		}
		public String getIdentificationForIPv6() {
			return identificationForIPv6;
		}
		public void setIdentificationForIPv6(String identificationForIPv6) {
			this.identificationForIPv6 = identificationForIPv6;
		}
		
		public SigXDRData reassemble(){
			//System.out.println("reassemble()---------sigXDRDataAgentList.size()------------"+sigXDRDataAgentList.size());

			//TODO 此处需要对sigXDRDataAgentList中的包按照offset排个序？？
			if(packgeType == 0)	{
				SigXDRData newSigXDRData = new SigXDRData();
				SigXDRData firstSigXDRData = sigXDRDataAgentList.get(0).getSigXDRData();
				newSigXDRData.setLen(firstSigXDRData.getLen());
				newSigXDRData.setVer(firstSigXDRData.getVer());
				newSigXDRData.setLinkType(firstSigXDRData.getLinkType());
				newSigXDRData.setCardType(firstSigXDRData.getCardType());
				newSigXDRData.setCardId(firstSigXDRData.getCardId());
				newSigXDRData.setTime_s(firstSigXDRData.getTime_s());
				newSigXDRData.setTime_ns(firstSigXDRData.getTime_ns());
				newSigXDRData.setLoad(firstSigXDRData.getLoad());//TODO IP\UDP相关的Length还是第一个分片的值，IP的flags也是第一个分片的值，目前用不到应该没问题 
				
				for( int i=1 ; i<sigXDRDataAgentList.size() ; i++){	
					int moveSize = sigXDRDataAgentList.get(i).getMoveSize();
					byte[] load = newSigXDRData.getLoad();
					byte[] load1 = sigXDRDataAgentList.get(i).getSigXDRData().getLoad();
					byte[] load1Sub = Arrays.copyOfRange(load1, 34+moveSize, load1.length);//reassemble时应该去掉后续包Enternet、IP,14+20=34字节(注意，第二个分片里没有UDP头了)
					byte[] newLoad = arraycat(load,load1Sub);
					newSigXDRData.setLoad(newLoad);
					newSigXDRData.setLen((short) (12+newLoad.length));					
				}
				return newSigXDRData;
			}else if(packgeType == 1){
				logger.error("TODO TO Support IPv6 without ESP !!");
				return null;
			}else if(packgeType == 2){
				
				SigXDRData newSigXDRData = new SigXDRData();
				SigXDRData firstSigXDRData = sigXDRDataAgentList.get(0).getSigXDRData();
				newSigXDRData.setLen(firstSigXDRData.getLen());
				newSigXDRData.setVer(firstSigXDRData.getVer());
				newSigXDRData.setLinkType(firstSigXDRData.getLinkType());
				newSigXDRData.setCardType(firstSigXDRData.getCardType());
				newSigXDRData.setCardId(firstSigXDRData.getCardId());
				newSigXDRData.setTime_s(firstSigXDRData.getTime_s());
				newSigXDRData.setTime_ns(firstSigXDRData.getTime_ns());
				newSigXDRData.setLoad(firstSigXDRData.getLoad());//TODO IP\UDP相关的Length还是第一个分片的值，IP的flags也是第一个分片的值，目前用不到应该没问题 
				
				for( int i=1 ; i<sigXDRDataAgentList.size() ; i++){	
					int moveSize = sigXDRDataAgentList.get(i).getMoveSize();
					byte[] load = newSigXDRData.getLoad();
					byte[] load1 = sigXDRDataAgentList.get(i).getSigXDRData().getLoad();
					byte[] load1Sub = Arrays.copyOfRange(load1, 62+moveSize, load1.length);//reassemble时应该去掉后续包Enternet、IPv6、ESP头域的14+40+8=62字节(注意，第二个分片里没有UDP头了)
					byte[] newLoad = arraycat(load,load1Sub);
					newSigXDRData.setLoad(newLoad);
					
					newSigXDRData.setLen((short) (12+newLoad.length));					
				}
				return newSigXDRData;
				
			}else{
				return null;
			}
			
		}
	}
	
	public class SigXDRDataAgent{
		int identification;
		String identificationForIPv6;
		int flags;
		int offset;
		SigXDRData sigXDRData;
		int moveSize = 0;
		int packgeType = 0;//0-IPv4(default),1-IPv6,2-IPv6+ESP
		
		public SigXDRDataAgent(int identification,int flags,int offset,SigXDRData sigXDRData,int moveSize){
			this.identification = identification;
			this.flags = flags;
			this.offset = offset;
			this.sigXDRData = sigXDRData;
			this.moveSize = moveSize;
		}
		
		public SigXDRDataAgent(String identificationForIPv6,int flags,int offset,SigXDRData sigXDRData,int moveSize,int packgeType){
			this.identificationForIPv6 = identificationForIPv6;
			this.flags = flags;
			this.offset = offset;
			this.sigXDRData = sigXDRData;
			this.moveSize = moveSize;
			this.packgeType = packgeType;
		}
		
		public int getIdentification() {
			return identification;
		}
		public int getFlags() {
			return flags;
		}
		public int getOffset() {
			return offset;
		}
		public SigXDRData getSigXDRData() {
			return sigXDRData;
		}
		public int getMoveSize() {
			return moveSize;
		}
		public int getPackgeType() {
			return packgeType;
		}
		public String getIdentificationForIPv6() {
			return identificationForIPv6;
		}
		
	}	
	
	/**
	 * convert byte[] to int
	 * @param bytes
	 * @return
	 */
	public static int bytesToInt(byte[] bytes){
		String bytesStr = bytesToStringByBit(bytes);
		Integer bytesInteger = Integer.valueOf(bytesStr,2);
		return bytesInteger;
	}
	
	/**
	 * byte[] convert to String by bit
	 * 
	 * @param bytes
	 * @return
	 */
	public static String bytesToStringByBit(byte[] bytes){
		StringBuffer bytesStr = new StringBuffer();
		for( byte b : bytes ){
			bytesStr.append(byteToStringByBit(b));
		}
		return bytesStr.toString();
	}
	
	/**
	 * byte convert to String by bit
	 * 返回单字节每一位的实际情况
	 * @param b
	 * @return
	 */
	public static String byteToStringByBit(byte b){
		StringBuffer sb = new StringBuffer();
		for (int i = 7; i >= 0; i--) {
			String bitStr = Byte.toString((byte) (b >> i & 1));
            sb.append(bitStr);
        }
		return sb.toString();
	}
	
	/**
	 * 组装两个byte[]
	 * @param buf1
	 * @param buf2
	 * @return
	 */
	public static byte[] arraycat(byte[] buf1,byte[] buf2){
		byte[] bufret=null;
		int len1=0;
		int len2=0;
		if(buf1!=null)
			len1=buf1.length;
		if(buf2!=null)
			len2=buf2.length;
		if(len1+len2>0)
			bufret=new byte[len1+len2];
		if(len1>0)
			System.arraycopy(buf1,0,bufret,0,len1);
		if(len2>0)
			System.arraycopy(buf2,0,bufret,len1,len2);
		return bufret;
	}
}
